package com.adnaan.backend.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * Redis缓存配置类
 * 配置Redis作为Spring Cache的缓存管理器
 *
 * @author adnaan
 * @since 2025-01-12
 */
@Configuration
@EnableCaching
@EnableConfigurationProperties(CacheProperties.class)
public class CacheConfig {

    /**
     * 配置RedisTemplate
     * 用于操作Redis的工具类，可以对Redis进行各种操作
     *
     * @param factory Redis连接工厂
     * @return RedisTemplate对象
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        // 创建RedisTemplate对象
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        // 设置连接工厂
        template.setConnectionFactory(factory);

        // 创建JSON序列化工具
        RedisSerializer<Object> serializer = redisSerializer();

        // 设置key的序列化方式
        template.setKeySerializer(RedisSerializer.string());
        // 设置value的序列化方式
        template.setValueSerializer(serializer);
        // 设置hash的key的序列化方式
        template.setHashKeySerializer(RedisSerializer.string());
        // 设置hash的value的序列化方式
        template.setHashValueSerializer(serializer);
        // 设置支持事务
        template.setEnableTransactionSupport(true);
        // 初始化RedisTemplate
        template.afterPropertiesSet();

        return template;
    }

    /**
     * 配置缓存管理器
     *
     * @param factory Redis连接工厂
     * @return 缓存管理器
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        // 创建Redis序列化器
        RedisSerializer<Object> serializer = redisSerializer();

        // 创建缓存配置
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                // 设置默认超时时间
                .entryTtl(Duration.ofHours(1))
                // 设置key的序列化方式
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()))
                // 设置value的序列化方式
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
                // 不缓存null值
                .disableCachingNullValues()
                // 设置key的前缀
                .prefixCacheNameWith("cache:");

        // 设置不同cache space的过期时间
        Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
        configMap.put("permissionTree", config.entryTtl(Duration.ofHours(24))); // 权限树缓存24小时
        configMap.put("userCache", config.entryTtl(Duration.ofHours(12))); // 用户缓存12小时
        configMap.put("commonCache", config.entryTtl(Duration.ofHours(1))); // 通用缓存1小时

        // 创建缓存管理器
        return RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .withInitialCacheConfigurations(configMap)
                .transactionAware()
                .build();
    }

    /**
     * 创建Redis序列化器
     * 使用GenericJackson2JsonRedisSerializer替代Jackson2JsonRedisSerializer
     *
     * @return Redis序列化器
     */
    private RedisSerializer<Object> redisSerializer() {
        // 创建ObjectMapper
        ObjectMapper objectMapper = new ObjectMapper();

        // 配置ObjectMapper
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 添加Java8时间模块支持
        objectMapper.registerModule(new JavaTimeModule());
        // 禁用时间戳序列化
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        // 配置序列化时包含类信息
        objectMapper.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL,
                JsonTypeInfo.As.PROPERTY
        );
        // 允许未知属性
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        // 创建GenericJackson2JsonRedisSerializer
        return new GenericJackson2JsonRedisSerializer(objectMapper);
    }
}